package com.zeyuan.rpcclient.netty.helloworld01;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.nio.charset.Charset;
import java.util.Iterator;

public class WriteBigDataServer {
    /*
    * selector不阻塞的时机：
    *   事件发生时:客户端连接触发accept时间，发送数据触发read，channel可写触发write
    * selector.wakeup();
    * selector.close();
    * selector所在线程被interrupt
    *
    * */
    public static void main(String[] args) throws IOException {
        ServerSocketChannel ssc = ServerSocketChannel.open();
        ssc.configureBlocking(false); //selector的时候，channel必须工作在非阻塞模式下
        Selector selector = Selector.open();
        //Seletor只针对网络io，文件io是不行的，比如filechannel。 selector保证有连接事件才连，有可读事件才读，有可写事件才写
        ssc.register(selector,SelectionKey.OP_ACCEPT); //selector与channel绑定，绑定事件类型：connect,acccept,read,write
        ssc.bind(new InetSocketAddress("localhost",8080));
        while (true){
            selector.select(); //阻塞直到绑定事件发生，也可以指定超时时间
            Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
            while (iterator.hasNext()){
                SelectionKey next = iterator.next();
                iterator.remove();
                if (next.isAcceptable()){
                    System.out.println("触发连接事件");
//                    SocketChannel channel = (SocketChannel) next.channel();
                    SocketChannel channel = ssc.accept();
                    channel.configureBlocking(false);  //就是上面的ssc
                    SelectionKey scKey = channel.register(selector, 0, null);
                    //向客户端发送大量数据
                    StringBuilder sb=new StringBuilder();
                    for (int i=0;i<3000000;i++){
                        sb.append("a");
                    }
                    ByteBuffer buffer = Charset.defaultCharset().encode(sb.toString());
//                    while (buffer.hasRemaining()){ //没发完一直循环，效率很低,可以写的时候再写效率高
//                        int write = channel.write(buffer);//不能保证一次把所有字符串都写入客户端,返回值代表实际写入的字节数
//                        System.out.println("实际写入字节数:"+write);
//                    }
                    int write = channel.write(buffer); //如果发送的数据大于buffer缓冲区会触发多次读取事件
                    System.out.println(write);
                    while (buffer.hasRemaining()){
                        //关注可写事件
                        scKey.interestOps(scKey.interestOps()+SelectionKey.OP_WRITE); //保持原有的基础上在新增可写事件监听  或scKey.interestOps() | SelectionKey.OP_WRITE
                        //把未写完的内容挂到sckey上，可写的时候把未写完的内容进行写入，已写入内容也要复制所以要attach
                        scKey.attach(buffer);
                    }

                }else if (next.isWritable()){ //新增了op_write的关注，才能有这个逻辑 。比原来好的地方就是发生op_write事件的时候才会进行写入，而不是一直while(buffer.hasRemaining())循环。相当于把while循环变成了对write事件的监听
                    ByteBuffer buffer = (ByteBuffer) next.attachment(); //带有连接的时候写入的数据，现在需要写入未写完的数据.这个附件是连接的时候才附加进去的，要传输肯定要先连接，所以不用考虑这个附件是否存在
                    SocketChannel sc = (SocketChannel) next.channel();
                    int write = sc.write(buffer);
                    System.out.println(write);
                    //清理buffer，如果buffer没有可写内容了，一直挂在skey上也不好
                    if (!buffer.hasRemaining()){
                        next.attach(null);
                        next.interestOps(next.interestOps()-SelectionKey.OP_WRITE); //因为写完了，所以去除op_write的监听
                    }
                }
            }
        }

    }
}
